package main.naiveBayes;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;

import y.base.Edge;
import y.base.EdgeCursor;
import y.base.Node;

import main.graph.FormTree;
import main.graph.GraphAlgo;
import main.graph.ValuedGraph;
import main.model.Attribute;
import main.model.NodeAndLabel;
import main.model.OntoEle;

/**
 * algorithms for finding correspondences from form tree to ontology
 * 
 * @author YuanAn
 *
 */
public class FindCorrespondence {

	/**
	 * get collection of sets of fixed nodes corresponding to the set of given labels
	 * 
	 * @param g
	 * @param labels
	 * @return
	 */
	public static ArrayList<ArrayList<Node>> getFixedNodes(ValuedGraph g, ArrayList<String> labels){
		
		ArrayList<ArrayList<Node>> tempResult = new ArrayList<ArrayList<Node>>();
		
		for(String label: labels){
			
			//the corresponding nodes
			ArrayList<Node> nodes = new ArrayList<Node>();
			
			//get the set of elements
			ArrayList eles = g.getElementsByLabel(label);
			for(int i = 0; i < eles.size(); i++){
				Object ele = eles.get(i);
				if(ele instanceof Node){
					if(!nodes.contains((Node) ele))
							nodes.add((Node) ele);
				}
				if(ele instanceof Edge){
					Node target = ((Edge)ele).target();
					if(!nodes.contains(target))
						nodes.add(target);
				}
			}
			
			tempResult.add(nodes);
			
		}
		
		ArrayList<ArrayList<Node>> combinations = GraphAlgo.getCombination(tempResult);
		
		ArrayList<ArrayList<Node>> result = new ArrayList<ArrayList<Node>>();
		
		//remove the list of nodes such that an attribute node is separated from the entity node
		for(ArrayList<Node> list: combinations){
			if(!hasIsolatedAttribute(g,list))
				if(!containsFixedSet(result, list))
					result.add(list);
		}
		
		return result;
	}
	
	/**
	 * get collection of sets of fixed nodes corresponding to the set of given labels
	 * 
	 * @param g
	 * @param labels
	 * @return
	 */
	public static ArrayList<ArrayList<NodeAndLabel>> getFixedNoddAndLabels(ValuedGraph g, ArrayList<String> labels){
		
		ArrayList<ArrayList<NodeAndLabel>> tempResult = new ArrayList<ArrayList<NodeAndLabel>>();
		
		for(String label: labels){
			
			//the corresponding nodes
			ArrayList<NodeAndLabel> nodeAndLabels = new ArrayList<NodeAndLabel>();
			
			ArrayList<Node> nodes = getSetOfNodesFromLabel(g, label);
			
			for(Node node: nodes){
				NodeAndLabel aNodeAndLabel = new NodeAndLabel(node, label);
				nodeAndLabels.add(aNodeAndLabel);
			}
			
			tempResult.add(nodeAndLabels);
			
		}
		
		ArrayList<ArrayList<NodeAndLabel>> combinations = GraphAlgo.getCombination(tempResult);
		
		ArrayList<ArrayList<NodeAndLabel>> result = new ArrayList<ArrayList<NodeAndLabel>>();
		
		//remove the list of nodes such that an attribute node is separated from the entity node
		
		for(ArrayList<NodeAndLabel> listNodeAndLabel: combinations){
			
			ArrayList<Node> listNodes = new ArrayList<Node>();
			for(NodeAndLabel nodeAndLabel: listNodeAndLabel)
				listNodes.add(nodeAndLabel.getNode());
			
			if(!hasIsolatedAttribute(g,listNodes))
				if(!containsFixedSetNodeAndLabel(result, listNodeAndLabel))
					result.add(listNodeAndLabel);
		}
		
		return result;
	}
	
	public static ArrayList<Node> getSetOfNodesFromLabel(ValuedGraph g, String treeLabel){
		
		HashMap<String, ArrayList<String>> dictionary = getCorrespondences("resources/correspondences");
		
		ArrayList<String> labels = dictionary.get(treeLabel);
		
		ArrayList<Node> nodes = new ArrayList<Node>();
		
		//get the set of elements
		for(String label: labels){
			ArrayList eles = g.getElementsByLabel(label);
			for(int i = 0; i < eles.size(); i++){
				Object ele = eles.get(i);
				if(ele instanceof Node){
					if(!nodes.contains((Node) ele))
							nodes.add((Node) ele);
				}
				if(ele instanceof Edge){
					Node target = ((Edge)ele).target();
					if(!nodes.contains(target))
						nodes.add(target);
				}
			}
		}
		
		return nodes;
	}
	
	/**
	 * simulating a matching tool
	 * 
	 * @param file
	 * @return
	 */
	public static HashMap<String, ArrayList<String>> getCorrespondences(String file){
		
		HashMap<String, ArrayList<String>> ans = new HashMap<String, ArrayList<String>>();
		
		try{
			BufferedReader in = new BufferedReader(new FileReader(new File(file)));
			
			String line;
			
			while((line = in.readLine()) != null){
				//a line is in the form: [Vehicle] :=: [vso:Vehicle]
				String[] eles = line.split(":=:");
				if(eles.length == 2){
					String keyString = eles[0].trim();
					String key = keyString.substring(1,keyString.length() - 1);
					String valueString = eles[1].trim();
					String value = valueString.substring(1, valueString.length() - 1);
					
					ArrayList<String> corresps = ans.get(key);
					
					if(corresps != null){
						if(!corresps.contains(value))
							corresps.add(value);
					}
					else{
						ArrayList<String> newCorresps = new ArrayList<String>();
						newCorresps.add(value);
						ans.put(key, newCorresps);
					}
				}
			}
			
		}
		catch(Exception e){
			e.printStackTrace();
		}
		
		
		return ans;
	}
	
	/**
	 * check whether the list of nodes contains attribute nodes that are isolated from entity nodes.
	 * @param list
	 * @return
	 */
	private static boolean hasIsolatedAttribute(ValuedGraph g, ArrayList<Node> list){
		
		for(Node node: list){
			OntoEle value = (OntoEle) g.getNodeValue(node);
			if(value instanceof Attribute){
				for(EdgeCursor ec = node.edges(); ec.ok(); ec.next()){
					Edge edge = ec.edge();
					Node opp = edge.opposite(node);
					
					if(!list.contains(node) || !list.contains(opp))
						return true;
				}
			}
		}
		
		return false;
	}
	
	/**
	 * check if the list is already in the list of lists
	 * 
	 * @param lists
	 * @param list
	 * @return
	 */
	private static boolean containsFixedSet(ArrayList<ArrayList<Node>> lists, ArrayList<Node> list){
		
		if(lists.isEmpty())
			return false;
		
		for(ArrayList<Node> existingList: lists)
			if(isSameLists(existingList, list))
				return true;
		
		return false;
	}
	
	private static boolean isSameLists(ArrayList<Node> first, ArrayList<Node> second){
		
		for(Node node: first)
			if(!second.contains(node))
				return false;
		
		for(Node node: second)
			if(!first.contains(node))
				return false;
		
		return true;
	}
	
	private static boolean containsFixedSetNodeAndLabel(ArrayList<ArrayList<NodeAndLabel>> lists, ArrayList<NodeAndLabel> list){
		
		if(lists.isEmpty())
			return false;
		
		for(ArrayList<NodeAndLabel> existingList: lists)
			if(!isSameListsNodeAndLabel(existingList, list))
				return false; //correct one is true
		
		return true;//correct one is false
	}
	
	private static boolean isSameListsNodeAndLabel(ArrayList<NodeAndLabel> first, ArrayList<NodeAndLabel> second){
		
		ArrayList<Node> firstNodes = new ArrayList<Node>();
		for(NodeAndLabel nodeAndLabel: first)
			firstNodes.add(nodeAndLabel.getNode());
		
		ArrayList<Node> secondNodes = new ArrayList<Node>();
		for(NodeAndLabel nodeAndLabel: second)
			secondNodes.add(nodeAndLabel.getNode());
		
		for(Node node: firstNodes)
			if(!secondNodes.contains(node))
				return false;
		
		for(Node node: secondNodes)
			if(!firstNodes.contains(node))
				return false;
		
		return true;
	}
}
